package com.wg.net.peer;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;
import com.wg.core.lang.IDisposable;
import com.wg.core.thread.fiber.IFiber;
import com.wg.core.thread.fiber.PoolFiber;
import com.wg.net.ConnectionState;
import com.wg.net.DisconnectReason;
import com.wg.net.SendResult;
import com.wg.net.operations.*;
import com.wg.net.operations.base.OperationRequest;
import com.wg.net.operations.base.OperationResponse;
import com.wg.net.handle.HandleTcpClientRequestMgr;
import com.wg.net.protocols.IProtocol;
import com.wg.net.protocols.MessageHeader;
import com.wg.net.protocols.MessageType;
import com.wg.net.session.ISession;

import java.util.Arrays;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 网络层对端基类（封装通用连接管理逻辑）
 * <p>
 * 职责：
 * - 管理连接状态（初始化、连接、断开）
 * - 处理消息的接收、发送与响应
 * - 提供资源释放与异常处理
 *
 * @author 少爷123
 */
public abstract class BasePeer implements IDisposable, IManagedPeer {
    private static final Log log = LogFactory.get();

    /**
     * 纤维调度器（异步任务执行）
     */
    protected final IFiber requestFiber;

    /**
     * 状态锁（保证状态转换的原子性）
     */
    protected final ReentrantLock lock = new ReentrantLock();

    /**
     * 连接状态（初始化、连接中、已连接、断开中、已断开）
     */
    protected ConnectionState connectionState = ConnectionState.Initializing;

    /**
     * 是否在断开时自动释放资源
     */
    protected volatile boolean disposeOnDisconnect = true;

    /**
     * 协议处理器（编解码、消息路由）
     */
    protected volatile IProtocol protocol;

    /**
     * 未管理对端引用（线程安全）
     */
    private final AtomicReference<ISession> unmanagedSessionRef = new AtomicReference<>();

    /**
     * 请求时间戳缓存（记录操作码对应的发送时间）
     */
    private final ConcurrentHashMap<Integer, Long> requestTimeMap = new ConcurrentHashMap<>(128);

    /**
     * 构造网络层对端（基础构造）
     *
     * @param initRequest 初始化请求
     */
    protected BasePeer(InitRequest initRequest) {
        this(initRequest.getPeer(), initRequest.getRequestFiber(), initRequest.getProtocol());
    }

    /**
     * 构造网络层对端（自定义会话与纤维）
     *
     * @param session  未管理对端会话
     * @param fiber    纤维调度器
     * @param protocol 协议处理器
     */
    protected BasePeer(ISession session, IFiber fiber, IProtocol protocol) {
        this.requestFiber = fiber != null ? fiber : new PoolFiber();
        this.setUnmanagedSession(session);
        this.protocol = protocol;
        this.connectionState = ConnectionState.Initializing;
        this.getUnmanagedSession().setUserData(this); 
        this.requestFiber.start(); 
    }

    /**
     * 构造网络层对端（自定义状态）
     *
     * @param protocol        协议处理器
     * @param session         未管理对端会话
     * @param connectionState 初始连接状态
     */
    protected BasePeer(IProtocol protocol, ISession session, ConnectionState connectionState) {
        this(session, null, protocol);
        this.connectionState = connectionState;
    }

    @Override
    public boolean applicationOnDisconnect(DisconnectReason reasonCode, String reasonDetail) {
        this.requestFiber.enqueue(() -> onApplicationDisconnect(reasonCode, reasonDetail));
        return true;
    }

    @Override
    public void applicationOnReceive(byte[] data) {
        doApplicationOnReceive(data);
    }

    @Override
    public String toString() {
        return StrUtil.format("[{}] 连接ID: {}, 本地: {}:{} , 远程: {}:{}",
                getClass().getSimpleName(),
                getConnectionId(),
                getLocalIp(), getLocalPort(),
                getRemoteIp(), getRemotePort());
    }

    @Override
    public void dispose() {
        boolean shouldClose = false;
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            switch (connectionState) {
                case Disconnected:
                    connectionState = ConnectionState.Disposed;
                    requestFiber.dispose(); // 释放纤维资源
                    break;
                case Connecting:
                case Connected:
                    connectionState = ConnectionState.DisposeDisconnecting;
                    shouldClose = true; // 需要关闭底层连接
                    break;
                case Disconnecting:
                    connectionState = ConnectionState.DisposeDisconnecting;
                    break;
                default:
                    break;
            }
        } finally {
            lock.unlock();
        }
        if (shouldClose) {
            getUnmanagedSession().close(); // 关闭底层连接
        }
    }

    public void close() {
        if (log.isDebugEnabled()) {
            log.debug("主动断开连接 | 对端: {}", this);
        }
        boolean shouldClose = false;
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            switch (connectionState) {
                case Connecting:
                case Connected:
                    connectionState = ConnectionState.Disconnecting;
                    shouldClose = true; // 需要关闭底层连接
                    break;
                default:
                    break;
            }
        } finally {
            lock.unlock();
        }
        if (shouldClose) {
            getUnmanagedSession().close(); // 关闭底层连接
        }
    }

    /**
     * 刷新发送缓冲区（强制发送待写数据）
     */
    public void flush() {
        getUnmanagedSession().flush();
    }

    /**
     * 发送调试字符串（用于日志追踪）
     *
     * @param msg 调试信息
     */
    public void sendDebugString(String msg) {
        getUnmanagedSession().send(msg.getBytes());
    }

    public long getConnectionId() {
        return getUnmanagedSession().getConnectionId();
    }

    public boolean isConnected() {
        return connectionState == ConnectionState.Connected;
    }

    public String getLocalIp() {
        return getUnmanagedSession().getLocalIp();
    }

    public int getLocalPort() {
        return getUnmanagedSession().getLocalPort();
    }

    public String getRemoteIp() {
        return getUnmanagedSession().getRemoteIp();
    }

    public int getRemotePort() {
        return getUnmanagedSession().getRemotePort();
    }

    public ISession getUnmanagedSession() {
        return unmanagedSessionRef.get();
    }

    public void setUnmanagedSession(ISession session) {
        this.unmanagedSessionRef.getAndSet(session);
    }

    public void setRequestTimeMap(int operationCode, long timestamp) {
        requestTimeMap.put(operationCode, timestamp);
    }

    public long getRequestTime(int operationCode) {
        return requestTimeMap.getOrDefault(operationCode, 0L);
    }

    /**
     * 实际处理接收消息（异步）
     *
     * @param data 接收到的字节数据
     */
    void doApplicationOnReceive(byte[] data) {
        requestFiber.enqueue(() -> onReceiveInternal(data));
    }

    /**
     * 实际处理断开事件（异步）
     *
     * @param reasonCode   断开原因代码
     * @param reasonDetail 断开详细说明
     */
    void onApplicationDisconnect(DisconnectReason reasonCode, String reasonDetail) {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            if (connectionState != ConnectionState.DisposeDisconnecting) {
                connectionState = ConnectionState.Disconnected;
            } else {
                connectionState = ConnectionState.Disposed;
                requestFiber.dispose(); // 释放纤维资源
            }
        } finally {
            lock.unlock();
        }

        onDisconnect(reasonCode, reasonDetail); // 触发业务层断开回调
        if (disposeOnDisconnect) {
            dispose(); // 自动释放资源
        }
    }

    /**
     * 初始化连接（应用层调用）
     *
     * @param initRequest 初始化请求
     */
    public void initialize(InitRequest initRequest) {
        if (connectionState == ConnectionState.Initializing) {
            connectionState = ConnectionState.Connected;
            sendInitResponse(initRequest); // 发送初始化响应
        }
    }

    /**
     * 发送初始化响应（内部逻辑）
     *
     * @param initRequest 初始化请求
     */
    private void sendInitResponse(InitRequest initRequest) {
        InitResponse initResponse = new InitResponse();
        initResponse.setPeerId(getUnmanagedSession().getPeerId());

        OperationResponse response = new OperationResponse(OperationCode.INIT_REQUEST, initResponse);
        byte[] data = initRequest.getProtocol().serializeInitResponse(response);
        SendResult sendResult = initRequest.getPeer().send(data);
        initRequest.getPeer().flush();

        if (log.isDebugEnabled()) {
            log.debug("初始化响应已发送 | 连接ID: {}, 发送结果: {}",
                    initRequest.getPeer().getConnectionId(), sendResult);
        }
    }

    /**
     * 实际处理接收数据（内部逻辑）
     *
     * @param data 接收到的字节数据
     */
    void onReceiveInternal(byte[] data) {
        try {
            onReceive(data); // 触发业务层数据处理
        } catch (Exception e) {
            log.error("数据处理异常 | 对端: {}, 数据: {}", this, Arrays.toString(data), e);
        }
    }

    /**
     * 业务层数据处理入口
     *
     * @param data 接收到的字节数据
     */
    protected void onReceive(byte[] data) {
        MessageHeader header = this.protocol.parseMessageHeader(data);
        if (header == null) {
            this.onUnexpectedDataReceived(data, " 解析消息头失败");
            return;
        }

        OperationRequest request = this.protocol.parseOperationRequest(data, header.isZip());
        MessageType messageType = header.getMessageType();
        if (messageType == MessageType.InternalOperationRequest) {
            this.onInternalOperationRequest(request);
        } else if (messageType == MessageType.Operation) {
            if (request != null) {
                this.onOperationRequest(request);
            } else {
                this.onUnexpectedDataReceived(data, " 解析操作请求失败");
            }
        }
    }

    protected void onUnexpectedDataReceived(byte[] data, String debugMessage) {
        log.warn("断开对端连接: {} 接收到意外数据。失败原因: {} 数据: {}", this, debugMessage, Arrays.toString(data));
        this.close();
    }

    /**
     * 处理操作请求
     *
     * @param request 操作
     */
    protected void onOperationRequest(OperationRequest request) {
        HandleTcpClientRequestMgr.getInstance().execute(this, request);
    }

    /**
     * 断开事件
     *
     * @param reasonCode   编号
     * @param reasonDetail 说明
     */
    protected abstract void onDisconnect(DisconnectReason reasonCode, String reasonDetail);

    /**
     * 处理内部操作请求（如心跳、ping）
     *
     * @param request 操作请求对象
     */
    protected void onInternalOperationRequest(OperationRequest request) {
        switch (request.getOpCode()) {
            case 0: // 保留操作码
                break;
            case 1: // Ping请求
                handlePingRequest(request);
                break;
            default:
                log.warn("未知内部操作码: {}", request.getOpCode());
                break;
        }
    }

    /**
     * 处理Ping请求
     *
     * @param request Ping请求对象
     */
    private void handlePingRequest(OperationRequest request) {
        PingRequest pingRequest = new PingRequest(request);
        long clientTimestamp = pingRequest.getClientTimeStamp();
        long serverTimestamp = DateUtil.currentSeconds();
        PingResponse pingResponse = new PingResponse(clientTimestamp, serverTimestamp);

        OperationResponse response = new OperationResponse(request.getOpCode(), pingResponse);
        sendInternalOperationResponse(response);
    }

    /**
     * 发送内部操作响应（内部逻辑）
     *
     * @param operationResponse 操作响应对象
     */
    private void sendInternalOperationResponse(OperationResponse operationResponse) {
        if (operationResponse == null) {
            throw new IllegalArgumentException("操作响应不能为空");
        }

        byte[] data = protocol.serializeOperationResponse(operationResponse, MessageType.InternalOperationResponse);
        SendResult sendResult = sendData(data);
        logOperationResponse(operationResponse, data, sendResult);
    }

    /**
     * 发送操作响应（业务层调用）
     *
     * @param operationResponse 操作响应对象
     * @return 发送结果
     */
    public SendResult sendOperationResponse(OperationResponse operationResponse) {
        if (operationResponse == null) {
            throw new NullPointerException("操作响应不能为空");
        }

        if (connectionState == ConnectionState.Initializing) {
            throw new IllegalStateException("对端未初始化，无法发送响应");
        }

        byte[] data = protocol.serializeOperationResponse(operationResponse, MessageType.OperationResponse);
        if (data == null) {
            log.error("序列化操作响应失败 | 操作响应: {}", operationResponse);
            return SendResult.Failed;
        }

        SendResult sendResult = sendData(data);
        logOperationResponse(operationResponse, data, sendResult);
        return sendResult;
    }

    /**
     * 实际发送数据（内部逻辑）
     *
     * @param data 待发送的字节数据
     * @return 发送结果
     */
    protected SendResult sendData(byte[] data) {
        if (connectionState != ConnectionState.Connected) {
            onSendFailed(SendResult.Disconnected, data.length);
            return SendResult.Disconnected;
        }

        SendResult sendResult = getUnmanagedSession().send(data);
        switch (sendResult) {
            case EncryptionNotSupported:
            case Disconnected:
            case Failed:
                onSendFailed(sendResult, data.length);
                break;
            case Ok:
                onSend(data.length);
                break;
            case SendBufferFull:
                onSendBufferFull();
                break;
            default:
                break;
        }
        return sendResult;
    }

    /**
     * 发送成功回调（业务层可重写）
     *
     * @param length 发送数据长度
     */
    protected void onSend(int length) {
        // 可选：记录发送成功的统计信息
    }

    /**
     * 发送缓冲区满回调（业务层可重写）
     */
    protected void onSendBufferFull() {
        if (connectionState == ConnectionState.Connected) {
            log.info("发送缓冲区已满，断开连接 | 对端: {}", this);
            close();
        }
    }

    /**
     * 发送失败回调（业务层可重写）
     *
     * @param sendResult  发送结果
     * @param messageSize 消息大小
     */
    protected void onSendFailed(SendResult sendResult, int messageSize) {
        if (sendResult != SendResult.Disconnected && log.isWarnEnabled()) {
            log.warn("发送失败 | 对端: {}, 原因: {}, 消息大小: {}字节",
                    this, sendResult, messageSize);
        }
    }

    /**
     * 日志记录操作响应（调试信息）
     *
     * @param response   操作响应对象
     * @param data       发送的字节数据
     * @param sendResult 发送结果
     */
    protected void logOperationResponse(OperationResponse response, byte[] data, SendResult sendResult) {
        if (log.isDebugEnabled()) {
            log.debug("操作响应已发送 | 对端: {}, 响应: {}, 结果: {}, 数据大小: {}字节",
                    this, response, sendResult, data.length);
        }
    }
}